/**
 * 
 */
package name.yzhu.httpserver;

import java.net.BindException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.nio.SelectChannelConnector;
import org.eclipse.jetty.util.thread.QueuedThreadPool;

import name.yzhu.common.service.IServer;

/**
 * @author yhzhu
 *
 */
public abstract class AbstractHttpServer implements IServer {
	private static final Log LOG = LogFactory.getLog(AbstractHttpServer.class);
	private static final String CONNECTED_NAME = "only connected ";

	protected Server server = null;
	private int port = 10020;
	private String host = null;
	private int acceptQueueSize = 50;
	private int threadPoolSize = 5;
	private int maxIdelTimeinMilli=-1;
	private volatile boolean joining = false;
	
	protected abstract void initialContexts() ;
	
	protected abstract ServerType getServerType();
	
	public AbstractHttpServer() {
		server = new Server();
	}

	private void initialConnection() throws Exception {
		SelectChannelConnector connector = new SelectChannelConnector();
		connector.setPort(port);
		if (host != null)
			connector.setHost(host);
		connector.setName(CONNECTED_NAME);
		connector.setAcceptQueueSize(acceptQueueSize);
		connector.setAcceptorPriorityOffset(1);
		connector.setAcceptors(Runtime.getRuntime().availableProcessors()/2 + 1);
		int poolSize=Math.max(connector.getAcceptors()*3, threadPoolSize);

		if(poolSize!=this.threadPoolSize){
			LOG.info("The pool size will be set:"+poolSize);
		}
		QueuedThreadPool qtp=new QueuedThreadPool(poolSize);
		connector.setThreadPool(qtp);
		qtp.start();

		if(this.maxIdelTimeinMilli!=-1){
			connector.setMaxIdleTime(maxIdelTimeinMilli);
		}

		server.setConnectors(new Connector[] {connector});
	}

	@Override
	public void start() throws ServerException {
		try {
			initialConnection();

		    initialContexts();

		    server.start();
		    LOG.info("Server ["+getServerType()+"] started on host: "+host+" port:"+port);

		}catch(BindException e) {
			LOG.error("port is being used ", e);
			throw new ServerException(e);
		} catch (Exception e) {
			LOG.error("Start server failed host: "+host+" port ["+port+"]", e);
			throw new ServerException(e);
		}
	}

	public void join() throws ServerException {
		if (joining)
			return;
		try {
			joining = true;
			LOG.debug("server joining ....");
			server.join();
			LOG.info("server joined ....");
		} catch (InterruptedException e) {
			throw new ServerException(e);
		}
		finally{
			joining = false;
		}
	}

	public void stop() throws ServerException {
		try {
			LOG.debug("server stopping ....");
			server.stop();
			LOG.info("server stopped ....");
		} catch (Exception e) {
			throw new ServerException(e);
		}
	}

	@Override
	public boolean isRunning() throws Exception {
		return server.isRunning() ;
	}

	@Override
	public boolean isJoining() {
		return joining ;
	}
	
	public void getBebPathServlets(){

	}

	public boolean isStarted() {
		return server.isStarted();
	}

	/**
	 * @return the port
	 */
	public int getPort() {
		return port;
	}

	/**
	 * @param port the port to set
	 */
	public void setPort(int port) {
		this.port = port;
	}

	/**
	 * @return the threadPoolSize
	 */
	public int getThreadPoolSize() {
		return threadPoolSize;
	}

	/**
	 * @param threadPoolSize the threadPoolSize to set
	 */
	public void setThreadPoolSize(int threadPoolSize) {
		this.threadPoolSize = threadPoolSize;
	}

	/**
	 * @return the acceptQueueSize
	 */
	public int getAcceptQueueSize() {
		return acceptQueueSize;
	}

	/**
	 * @param acceptQueueSize the acceptQueueSize to set
	 */
	public void setAcceptQueueSize(int acceptQueueSize) {
		this.acceptQueueSize = acceptQueueSize;
	}

	/**
	 * @return the maxIdelTimeinMilli
	 */
	public int getMaxIdelTimeinMilli() {
		return maxIdelTimeinMilli;
	}

	/**
	 * @param maxIdelTimeinMilli the maxIdelTimeinMilli to set
	 */
	public void setMaxIdelTimeinMilli(int maxIdelTimeinMilli) {
		this.maxIdelTimeinMilli = maxIdelTimeinMilli;
	}
	public String getHost() {
		return host;
	}
	public void setHost(String host) {
		this.host = host;
	}

	enum ServerType{
		Handle,
		Servlet
	}
}
